package middleware

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"gitee.com/go_love/setting"
	"gitee.com/xmcommon/mlog"
	"gitee.com/xmcommon/public/code"
	"gitee.com/xmcommon/public/tools"
	"github.com/gin-gonic/gin"
	"net/http"
	"net/http/httputil"
	"reflect"
	"strings"
	"time"
)

func CheckToken(skipUrlForTokenArr, maybeCheckForTokenArr []string) gin.HandlerFunc {
	return func(c *gin.Context) {
		for _, url := range skipUrlForTokenArr {
			if strings.Contains(c.Request.URL.Path, url) {
				c.Next()
				return
			}
		}

		token := c.GetHeader("token")
		if token == "" {
			for _, url := range maybeCheckForTokenArr {
				if strings.Contains(c.Request.URL.Path, url) {
					c.Next()
					return
				}
			}
			err := errors.New("invalid token, because token is empty")
			mlog.Log.Error(err.Error())
			c.JSON(http.StatusBadRequest, code.GeneralErrRet(err))
			c.Abort()
			return
		}
		//fmt.Printf("token = %s\n", token)
		if tools.FilteredSQLInject(token) {
			err := fmt.Errorf("sql注入攻击 %s", token)
			mlog.Log.Error(err.Error())
			c.JSON(http.StatusBadRequest, code.GeneralErrRet(err))
			c.Abort()
			return
		}
		uuid := ""
		var tokenCreateAt time.Time
		row := db.DbApp.QueryRow(`SELECT uuid, token_create_at FROM user_token WHERE token = ?;`, token)
		err := row.Scan(&uuid, &tokenCreateAt)
		if err != nil && err != sql.ErrNoRows {
			mlog.Log.Error(err.Error())
			c.JSON(http.StatusInternalServerError, code.GeneralErrRet(err))
			c.Abort()
		}

		//token是否过期
		if setting.RunningMode != enum.Dev {
			m, _ := time.ParseDuration(fmt.Sprintf("%ds", setting.TokenConf.TokenExp))
			//add := tokenCreateAt.Add(m)
			//now := time.Now()
			//fmt.Println("m: ", m)
			//fmt.Printf("add: %s\nnow: %s: %v\n", add.String(), now.String(), add.Before(now))
			//if uuid == "" || add.Before(now) {
			if uuid == "" || tokenCreateAt.Add(m).Before(time.Now()) {
				c.JSON(http.StatusOK, code.RetTokenExpiration)
				c.Abort()
				return
			}
		}
		c.Next()
	}
}

func CheckSQLInject(skipParamsForSQLInjectMap map[string]int) gin.HandlerFunc {
	return func(c *gin.Context) {
		body, err := httputil.DumpRequest(c.Request, true)
		if err != nil {
			err = fmt.Errorf("parse request body failed, err: %s", err)
			mlog.Log.Error(err.Error())
			c.JSON(http.StatusInternalServerError, code.GeneralErrRet(err))
			c.Abort()
			return
		}
		//fmt.Printf("%s\n", string(body))
		//fmt.Printf("%#v\n", c.Request)
		if bytes.Index(body, []byte("application/json")) != -1 { //检查json参数
			//过滤http头部,获取body
			body = body[bytes.Index(body, []byte("User-Agent")):]
			index := bytes.Index(body, []byte("{"))
			if index != -1 { //判读是否找到body
				body = body[index:]
			} else {
				body = nil
			}
			if len(body) != 0 { //检查request json参数
				fmt.Println(string(body))
				m := make(map[string]interface{})
				err = json.Unmarshal(body, &m)
				if err != nil {
					err = fmt.Errorf("parse request body failed, err: %s", err.Error())
					mlog.Log.Error(err.Error())
					c.JSON(http.StatusInternalServerError, code.GeneralErrRet(err))
					c.Abort()
					return
				}
				for k, v := range m {
					if skipParamsForSQLInjectMap[k] == 1 {
						continue
					}
					if v != nil && reflect.TypeOf(v).String() == "string" {
						if tools.FilteredSQLInject(v.(string)) {
							err := fmt.Errorf("sql注入攻击 %s %s", k, v)
							mlog.Log.Error(err.Error())
							c.JSON(http.StatusBadRequest, code.GeneralErrRet(err))
							c.Abort()
							return
						}
					}
				}
			}
		}
		if c.Request.Form == nil { //检查get和post中的参数
			c.Request.ParseMultipartForm(32 << 20)
		}
		for k, arr := range c.Request.Form {
			if c.Request.Method != http.MethodGet {
				fmt.Printf("%s=%v&", k, arr)
			}
			if skipParamsForSQLInjectMap[k] == 1 {
				continue
			}

			for _, v := range arr {
				if tools.FilteredSQLInject(v) {
					err := fmt.Errorf("sql注入攻击 %s %s", k, v)
					mlog.Log.Error(err.Error())
					c.JSON(http.StatusBadRequest, code.GeneralErrRet(err))
					c.Abort()
					return
				}
			}
			fmt.Println()
		}
		c.Next()
	}
}

func CheckCrawler() gin.HandlerFunc {
	return func(c *gin.Context) {
		//headers := c.Request.Header
		//if len(headers["Accept-Language"]) == 0 {
		//	c.JSON(http.StatusForbidden, "拒绝访问")
		//	c.Abort()
		//	return
		//}
		c.Next()
	}

}

func Cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		method := c.Request.Method

		c.Header("Access-Control-Allow-Origin", "*")
		c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token")
		c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
		c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
		c.Header("Access-Control-Allow-Credentials", "true")

		//放行所有OPTIONS方法
		if method == "OPTIONS" {
			c.AbortWithStatus(http.StatusNoContent)
		}
		// 处理请求
		c.Next()

	}
}
